module iTimer.util.Log;

import iTimer.Types;

private import
	tango.io.Stdout ;
private import  tango.stdc.stdio ;
private import tango.io.stream.Format ;
private import  tango.time.WallClock, tango.text.convert.TimeStamp ;

private Array bu1, bu2 ;

extern(Windows){
    void OutputDebugStringW(wchar* Format);
}
version(DEV) {
    private FormatOutput!(char) _file_log ;
}
struct vLog {
	static private {
		void*	_in, _out, _err ;
        
        version(DEV) {
            Mutex mux ;
        }
	}
	
	static void Init() {
		version( Windows) {
			_out	= GetStdHandle(STD_OUTPUT_HANDLE) ;
			_in	= GetStdHandle(STD_INPUT_HANDLE) ;
			_err	= GetStdHandle(STD_ERROR_HANDLE) ;
		} else {
			static assert(false);
		}
        bu1   = new Array(1024, 1024);
        bu2  = new Array(1024, 1024);
        version(DEV) {
            mux = new Mutex ;
            auto style = tango.io.device.File.File.WriteAppending ;
            style.share = tango.io.device.File.File.Share.Read ;
            _file_log   = new FormatOutput!(char) ( new tango.io.device.File.File( AppConf.app ~ `.log` , style )) ;
            .log!(__FILE__,__LINE__)("app started");
        }
	}
    
    static void Exit(){
        version(DEV) {
            mux.lock();
            scope(exit)  mux.unlock();
            if( _file_log !is null ) {
                log_time_now();
                _file_log("Exit").flush;
                _file_log.close;
                _file_log   = null ;
            }
        }
    }
	
	version(DEV_NONE) static void opCall(vBuffer bu) {
		char[] tmp	= cast(char[]) bu.slice;
		Stdout(tmp).flush ;
	}
    
    version(DEV) {
        
        private static void log_time_now() {
            char[512] _time_tmp;
            auto _time_txt   = tango.text.convert.TimeStamp.format(_time_tmp, WallClock.now );
            _file_log(_time_txt);
            _file_log(' ');
        }
        
        static void log(char[] file, int line, T...)(T t){
            mux.lock();
            scope(exit)  mux.unlock();
            
            Thread th	= Thread.getThis ;
            
            _file_log('\t');
            log_time_now();
            _file_log.format("({:x}:{}:{}) ", cast(void*)th, file[0..$-2], line) ;
            _file_log.format(t) ;
            _file_log("\r\n");
        }    
    }
}

version(DEV) {
    void log(char[] file, int line, T...)(T t){
        vLog.mux.lock();
        scope(exit)  vLog.mux.unlock();
        try{
            Thread th	= Thread.getThis;
            bu1.clear;
            scope _log = new FormatOutput!(char) (bu1);
    
            _log.format("({:x}:{}:{}) ", cast(void*)th, file[0..$-2], line) ;
            _log.format(t).flush ;
        
            auto _data    = cast(char[]) bu1.slice ;
            
            vLog.log_time_now();
            _file_log(_data) ;
            _file_log("\r\n") ;
            
            bu2.clear;
            bu2(_data);
            bu2(_data);
            bu2(_data);
            
            auto __out   = cast(ubyte[]) bu2.slice ;
            auto _out   = (cast(wchar*) __out.ptr ) [ 0 .. __out.length / wchar.sizeof ] ;
            uint eat;
            auto _ret    = Utf.toString16(_data, _out, &eat ) ;
            version(GUI){
                
            }else{
                Stdout.formatln("`{}`", _ret).flush ;
            }
            _out[ _ret.length ] = 0 ;
            
            OutputDebugStringW(_out.ptr);
        }catch(Exception e){
            version(GUI){
                
            }else{
                Stdout.formatln("{}", e.msg).flush ;
            }
        }
    }
}

